package com.wuyan.wurpc.fault.tolerant;

import cn.hutool.core.collection.CollUtil;
import com.wuyan.wurpc.RpcApplication;
import com.wuyan.wurpc.config.RpcConfig;
import com.wuyan.wurpc.fault.retry.RetryStrategy;
import com.wuyan.wurpc.fault.retry.RetryStrategyFactory;
import com.wuyan.wurpc.loadbalancer.LoadBalancer;
import com.wuyan.wurpc.loadbalancer.LoadBalancerFactory;
import com.wuyan.wurpc.model.RpcRequest;
import com.wuyan.wurpc.model.RpcResponse;
import com.wuyan.wurpc.model.ServiceMetaInfo;
import com.wuyan.wurpc.server.tcp.VertxTcpClient;
import lombok.extern.slf4j.Slf4j;

import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;

/**
 * 转移到其他服务节点 - 容错策略
 */
@Slf4j
public class FailOverTolerantStrategy implements TolerantStrategy {

    @Override
    public RpcResponse doTolerant(Map<String, Object> context, Exception e) {
        // todo 可自行扩展，获取其他服务节点并调用
        log.info("开启故障转移——————————————————————————————————");
        RpcRequest rpcRequest = (RpcRequest)context.get("rpcRequest");
        List<ServiceMetaInfo> serviceMetaInfoList = (List<ServiceMetaInfo>)context.get("serviceMetaInfoList");
        ServiceMetaInfo selectedServiceMetaInfo = (ServiceMetaInfo) context.get("selectedServiceMetaInfo");
        //移除失败节点
        removedFailNode(selectedServiceMetaInfo, serviceMetaInfoList);

        RpcConfig rpcConfig = RpcApplication.getRpcConfig();
        LoadBalancer loadBalancer = LoadBalancerFactory.getLoadBalancer(rpcConfig.getLoadBalancer());
        Map<String, Object> requestParamMap = new HashMap<>();
        requestParamMap.put("methodName", rpcRequest.getMethodName());
        RpcResponse rpcResponse = null;
        while(!serviceMetaInfoList.isEmpty() || rpcResponse != null){
            ServiceMetaInfo serviceMetaInfo = loadBalancer.select(requestParamMap, serviceMetaInfoList);
            System.out.println("获取节点：" + serviceMetaInfo);
            try {
                log.info("重试策略选择——————————————————————————————————");
                RetryStrategy retryStrategy = RetryStrategyFactory.getInstance(rpcConfig.getRetryStrategy());
                log.info("策略为：retryStrategy : {}", retryStrategy);
                rpcResponse = retryStrategy.doRetry(() ->
                        VertxTcpClient.doRequest(rpcRequest, serviceMetaInfo)
                );
            }catch (Exception exception){
                //容错机制
                log.info("失败移除节点：serviceMetaInfo" + serviceMetaInfo);
                removedFailNode(serviceMetaInfo, serviceMetaInfoList);
            }
        }
        //调用失败
        throw new RuntimeException("调用失败", e);
    }
    /**
     * 移除失败节点，可考虑下线
     */
    private void removedFailNode(ServiceMetaInfo serviceMetaInfo, List<ServiceMetaInfo> serviceMetaInfoList){
        if(CollUtil.isNotEmpty(serviceMetaInfoList)){
            Iterator<ServiceMetaInfo> iterator = serviceMetaInfoList.iterator();
            while(iterator.hasNext()){
                ServiceMetaInfo next = iterator.next();
                if(serviceMetaInfo.getServiceNodeKey().equals(next.getServiceNodeKey())){
                    iterator.remove();
                }
            }
        }
    }

}
